#pragma once

#include <thread>
#include <mutex>
#include <condition_variable>
#include <vector>
#include <atomic>
#include <string>
#include <tuple>
#include <functional>
#include "my_log.hpp"
#include "block_queue.hpp"
#include "basic_task.hpp"
//std::move(std::make_tuple(&worker::get_status, this)) std::tuple<char& (worker::*)(void), worker*> bind_args
template<size_t MAX_SIZE = 20>
class workers
{
public:
    class worker
    {
    public:
        worker()
            : _status('s')
            , _w_arr(nullptr)
            , _thr()
        {}

        worker(workers<MAX_SIZE>* w_arr)
            : _status('s')
            , _w_arr(w_arr)
            , _thr(&workers<MAX_SIZE>::worker_init, &_w_arr->_task_storger, std::move(std::bind(&worker::get_status, this)))
        {}

        worker(const worker& other) = delete;

        worker(worker&& other)
        {
            _status = other._status;
            _w_arr = other._w_arr;
            _thr.swap(other._thr);
        }

        const worker& operator=(worker&& other)
        {
            _status = other._status;
            _w_arr = other._w_arr;
            _thr.swap(other._thr);
            return *this;
        }

        char& get_status()
        {
            return _status;
        }
    private:
        char _status;
        workers<MAX_SIZE>* _w_arr;
        std::thread _thr;
    };
public:
    workers()
        : _workers()
        , _task_storger()
    {
        _workers.resize(MAX_SIZE);
        for (auto& em : _workers)
        {
            em = std::move(worker(this));
        }
    }

    void add_task(basic_task* t_ptr)
    {
        _task_storger.push(t_ptr);
    }
private:
    static void worker_init(block_queue<basic_task*, MAX_SIZE>* task_storger_ptr, std::function<char&()> get_status)
    {
        basic_task* t_ptr = nullptr;
        while (1)
        {
            task_storger_ptr->pop(t_ptr);
            #ifdef _MY_DEBUG
            lg.basic_print(level::DEBUG, std::make_tuple("thread allocated"));
            #endif
            get_status() = 'r';
            t_ptr->operator()();
             #ifdef _MY_DEBUG
            lg.basic_print(level::DEBUG, std::make_tuple("thread released"));
            #endif
            get_status() = 's';
        }
    }
private:
    std::vector<worker> _workers;
    block_queue<basic_task*, MAX_SIZE> _task_storger;
};